import { Component, EventEmitter, Injector, Output, ViewChild } from '@angular/core';
import { AppConsts } from '@shared/AppConsts';
import { AppComponentBase } from '@shared/common/app-component-base';
import { IdAndOrderOfGuid, NameValueDto, PagedResultDtoOfNameValueDto } from '@shared/service-proxies/service-proxies';
import { ModalDirective } from 'ngx-bootstrap/modal';
import { LazyLoadEvent } from 'primeng/api';
import { Paginator } from 'primeng/paginator';
import { Table } from 'primeng/table';
import { Observable } from 'rxjs';
import { merge as _merge } from 'lodash-es';
import { finalize } from 'rxjs/operators';
import { DateTime } from 'luxon';
import { timeStamp } from 'console';

export interface IOrderByModalOptions {
    title?: string;
    defaultParam: any;
    getDataFunc: (paras: any) => Observable<any>;
    getTitile: (item: any) => string;
    getTime: (item: any) => DateTime;
    getItemOrder: (item: any) => number;
    getTotolCount: (result: any) => number;
    getItems: (result: any) => any;
    swapOrderFunc: (paras: any) => Observable<any>;
    swapOrderParam: any;
}

class OrderByItems {
    id: string;
    title: string;
    time: DateTime;
    order: number;
    index: number;
    modify: boolean;
}

//For more modal options http://valor-software.com/ngx-bootstrap/#/modals#modal-directive

@Component({
    selector: 'order-by-modal',
    templateUrl: './order-by-modal.component.html'
})
export class OrderByModalComponent extends AppComponentBase {

    @ViewChild('modal', { static: true }) modal: ModalDirective;
    @ViewChild('dataTable', { static: true }) dataTable: Table;
    @ViewChild('paginator', { static: true }) paginator: Paginator;

    options: IOrderByModalOptions;

    isShown = false;
    saving = false;

    data: OrderByItems[] = [];

    selectedDatas: OrderByItems[] = [];

    toIndex: number = 0;
    moveStep: number = 0;

    @Output() callback: EventEmitter<any> = new EventEmitter<any>();
    private hasModified = false;

    constructor(
        injector: Injector
    ) {
        super(injector);
        //this.toNum = this.primengTableHelper.defaultRecordsCountPerPage;
    }

    configure(options: IOrderByModalOptions): void {
        this.options = _merge({}, { title: this.l('Order') }, options);
    }

    show(): void {
        if (!this.options) {
            throw Error('Should call CommonLookupModalComponent.configure once before CommonLookupModalComponent.show!');
        }
        this.selectedDatas = [];
        this.hasModified = false;
        this.toIndex = 0;
        this.moveStep = 0;
        this.modal.show();
    }

    refreshTable(): void {
        if (this.primengTableHelper.TryChangePage(this.paginator, this.paginator.getPage())) {
            return;
        }

        this.getRecordsIfNeeds(null);
    }

    close(): void {
        if(this.hasModified){
            this.callback.emit();
        }
        this.modal.hide();
    }

    shown(): void {
        this.isShown = true;
        this.getRecordsIfNeeds(null);
    }

    getRecordsIfNeeds(event?: LazyLoadEvent): void {
        if (!this.isShown) {
            return;
        }

        this.getRecords(event);
    }

    getRecords(event?: LazyLoadEvent): void {
        const maxResultCount = this.primengTableHelper.getMaxResultCount(this.paginator, event);
        const skipCount = this.primengTableHelper.getSkipCount(this.paginator, event);
        if (this.primengTableHelper.shouldResetPaging(event)) {
            if (this.primengTableHelper.TryChangePage(this.paginator, 0)) {
                return;
            }
        }

        this.primengTableHelper.showLoadingIndicator();

        let input = this.options.defaultParam;
        input.skipCount = skipCount;
        input.maxResultCount = maxResultCount;

        this.options
            .getDataFunc(input)
            .pipe(finalize(() => this.primengTableHelper.hideLoadingIndicator()))
            .subscribe(result => {
                this.primengTableHelper.totalRecordsCount = this.options.getTotolCount(result);

                let temp: OrderByItems[] = [];
                let items = this.options.getItems(result);
                for (let index = 0; index < items.length; index++) {
                    const element = items[index];
                    let o = new OrderByItems();
                    o.title = this.options.getTitile(element);
                    o.time = this.options.getTime(element);
                    o.order = this.options.getItemOrder(element);
                    o.id = element.id;
                    o.index = index;
                    o.modify = false;
                    temp.push(o);
                }

                this.data = temp;
                this.primengTableHelper.records = this.data;
                this.primengTableHelper.hideLoadingIndicator();
            });
    }

    save() {
        if (this.saving) {
            return;
        }
        if (!this.data) {
            return;
        }

        let idList = this.data.filter(t=>t.modify).map(t => new IdAndOrderOfGuid({
            id: t.id,
            order: t.order
        }));

        if (idList.length === 0) {
            return;
        }

        this.saving = true;
        let input = this.options.swapOrderParam;
        input.idOrderList = idList;

        this.options
            .swapOrderFunc(input)
            .pipe(finalize(() => this.saving = false))
            .subscribe(result => {
                this.notify.success(this.l('OrderSuccessSuccessfully'));
                this.refreshTable();
                this.hasModified = true;
            });
    }

    upSort(record: OrderByItems) {
        const index = record.index;
        if (index === 0) {
            this.notify.warn(this.l('AlreadyBeTheFirst'));
            return;
        }

        let newData: OrderByItems[] = this.data.slice(0);
        this.move(index, index - 1, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }
    moveToFirst(record: OrderByItems) {
        const index = record.index;
        if (index === 0) {
            this.notify.warn(this.l('AlreadyBeTheFirst'));
            return;
        }

        let newData: OrderByItems[] = this.data.slice(0);
        this.move(index, 0, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }
    downSort(record: OrderByItems) {
        const index = record.index;
        if (index === this.data.length - 1) {
            this.notify.warn(this.l('AlreadyBeTheLast'));
            return;
        }
        let newData: OrderByItems[] = this.data.slice(0);
        this.move(index, index + 1, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }

    moveToLast(record: OrderByItems) {
        const index = record.index;
        if (index === this.data.length - 1) {
            this.notify.warn(this.l('AlreadyBeTheLast'));
            return;
        }
        let newData: OrderByItems[] = this.data.slice(0);
        this.move(index, this.data.length - 1, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }

    multiMove() {
        if (this.selectedDatas.length === 0) {
            return;
        }
        let temp = this.getSelectedItem();

        // 选中项们 作为一个整体移动，toStep作为首项的目标下标
        if (this.toIndex + temp.length > this.data.length) {
            this.toIndex = this.data.length - temp.length;
        }

        if (this.toIndex < 0) {
            this.toIndex = 0;
        }

        let newData: OrderByItems[] = this.data.slice(0);
        this.multiMoveInternal(temp, this.toIndex, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }

    multiShift() {
        if (this.selectedDatas.length === 0) {
            return;
        }
        let temp = this.getSelectedItem();

        // 选中项们 作为一个整体移动
        if (temp[0].index + this.moveStep < 0) {
            this.moveStep = -1 * temp[0].index;
        } else if (temp[0].index + temp.length + this.moveStep > this.data.length) {
            this.moveStep = this.data.length - temp.length - temp[0].index;
        }

        let toIndex = temp[0].index + this.moveStep;
        let newData: OrderByItems[] = this.data.slice(0);
        this.multiMoveInternal(temp, toIndex, newData);
        this.data = newData;
        this.primengTableHelper.records = this.data;
    }

    private multiMoveInternal(temp: OrderByItems[], toIndex: number, newData: OrderByItems[]) {
        if(temp.length === 0){
            return;
        }

        const firstEle = temp[0];
        let firstEleIndex = newData.findIndex(t => t.id == firstEle.id);
        if (firstEleIndex > toIndex) {
            for (let index = 0; index < temp.length; index++) {
                const element = temp[index];
                let newIndex = newData.findIndex(t => t.id == element.id);
                this.move(newIndex, toIndex + index, newData);
            }
        } else if(firstEleIndex === toIndex){
            temp.shift();
            this.multiMoveInternal(temp, toIndex + 1, newData);
        }
        else {
            for (let index = temp.length - 1; index >= 0; index--) {
                const element = temp[index];
                let newIndex = newData.findIndex(t => t.id == element.id);
                this.move(newIndex, toIndex + index, newData);
            }
        }
    }

    private getSelectedItem(): OrderByItems[] {
        //缓存中的下标不一定准确
        let arr = this.selectedDatas.map(t => this.data.find(n => n.id === t.id));
        return Array.from(new Set(arr)).sort((a, b) => a.index - b.index);
    }
    private move(fromIndex: number, toIndex: number, data: OrderByItems[]) {
        if (fromIndex === toIndex) {
            return;
        }

        let tempOrder: number;
        let i: number;

        let selectedItem = data[fromIndex];
        if (fromIndex < toIndex) {
            //向下
            let preItemOrder: number;
            for (i = fromIndex; i <= toIndex; i++) {
                const currentItem = data[i];

                if (i > fromIndex) {
                    tempOrder = currentItem.order;
                    currentItem.order = preItemOrder;
                    preItemOrder = tempOrder;
                    currentItem.index = i - 1;
                    currentItem.modify = true;
                    data[i - 1] = currentItem;
                    if (i === toIndex) {
                        selectedItem.order = preItemOrder;
                        selectedItem.index = i;
                        selectedItem.modify = true;
                        data[i] = selectedItem;
                    }
                } else {
                    preItemOrder = currentItem.order;
                }
            }
        } else {
            //向上
            let preItemOrder: number;
            for (i = fromIndex; i >= toIndex; i--) {
                const currentItem = data[i];

                if (i < fromIndex) {
                    tempOrder = currentItem.order;
                    currentItem.order = preItemOrder;
                    preItemOrder = tempOrder;
                    currentItem.index = i + 1;
                    currentItem.modify = true;
                    data[i + 1] = currentItem;
                    if (i === toIndex) {
                        selectedItem.order = preItemOrder;
                        selectedItem.index = i;
                        selectedItem.modify = true;
                        data[i] = selectedItem;
                    }
                } else {
                    preItemOrder = currentItem.order;
                }
            }
        }
    }
}
